#include "ModuleMgr.h"

#include <auth/auth.h>
#include <bonddict/IBondDict.h>
#include <core/FileSys.h>
#include <core/Utility.h>

#include <QJsonArray>
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonValue>
#include <QLibrary>

ModuleMgr::ModuleMgr() { loadModules(); }

ModuleMgr::~ModuleMgr() {}

IModule* ModuleMgr::module(const QString& id) {
  auto iter = m_modules.find(id);
  if (iter != m_modules.end()) {
    if (iter->module == nullptr) iter->module = loadModule(iter->id);

    return iter->module;
  }

  return nullptr;
}

void ModuleMgr::unloadModules() {
  for (auto m : m_modules) {
    if (m.module) m.module->unInitialize();
  }
}

ModuleMgr& ModuleMgr::instance() {
  static ModuleMgr s_m;
  return s_m;
}

void ModuleMgr::loadModules() {
  QString cfgPath = FileSys::instance()->appCfgDir() + "/modules.cfg";
  QJsonDocument doc = CoreUtility::openJsonDoc(cfgPath);
  if (doc.isNull()) {
    logError(MODULE_NAME, "Load module configure failed!");
    return;
  }

  QJsonArray modules = doc.array();
  for (QJsonValue v : modules) {
    QJsonObject moduleObj = v.toObject();
    ModuleDef def;
    def.id = moduleObj["id"].toString();
    def.dllName = moduleObj["dll"].toString();
    def.dir = moduleObj["dir"].toString();
    def.loadBehavior = moduleObj["loadbehavior"].toInt();
    m_modules[def.id] = def;

    if (def.loadBehavior == 0) m_modules[def.id].module = loadModule(def.id);
  }
}

IModule* ModuleMgr::loadModule(const QString& id) {
  QString dllPath = FileSys::instance()->runRootDir() + "/" +
                    m_modules[id].dir + "/" + m_modules[id].dllName;
  QLibrary moduleDll(dllPath);
  if (!moduleDll.load()) {
    //   logError(MODULE_NAME, "Load Module Failed:fail to load library:
    //   %ls\n%s", dllPath.toStdWString().c_str(),
    //   moduleDll.errorString().toUtf8());
    return nullptr;
  }

  typedef IModule* (*getModule)();
  getModule funPtr = (getModule)moduleDll.resolve("getModule");
  if (funPtr == nullptr) {
    logError(MODULE_NAME, "Load Module %s Failed:fail to find export function!",
             id.toStdString().c_str());
    return nullptr;
  }

  IModule* module = funPtr();
  if (module) module->initialize(getAppInstance());

  return module;
}
